#include "ColorMaterial.h"
#include "DirectX\Shader.h"
#include "DirectX\RenderableEntity.h"
#include "DirectX\Device.h"
#include "DirectX\Camera.h"
#include "DirectX\Utility.h"
#include "DirectX\Model.h"

using namespace Graphics;

RTTI_DEFINITIONS(ColorMaterial)

#ifdef DX_11

ColorMaterial::ColorMaterial(void)
	:Graphics::Material("","")
{
}

ColorMaterial::~ColorMaterial(void)
{
}

void ColorMaterial::Initialize()
{
	Graphics::D3DDevice* nDevice = GetPlatform()->GetDevice()->As<Graphics::D3DDevice>();
	InitializeShader(nDevice, nDevice->GetWindowsSettings().WindowHandle, "..\\..\\assets\\shaders\\color.vs", "..\\..\\assets\\shaders\\color.ps");
}

void ColorMaterial::Deinitialize()
{
	mShader->Deinitialize();
}

bool ColorMaterial::InitializeShader(D3DDevice* device, HWND winHandle, const char* vertexShaderName, const char*  pixelShaderName)
{
	mShader->InitializeShader(device, winHandle, vertexShaderName, pixelShaderName);

	/* Now setup the layout of the data that goes into the shader.
	 This setup needs to match the VertexType stucture in the ModelClass and in the shader.*/
	D3D11_INPUT_ELEMENT_DESC polygonLayout[2];
	polygonLayout[0].SemanticName = "POSITION";
	polygonLayout[0].SemanticIndex = 0;
	polygonLayout[0].Format = DXGI_FORMAT_R32G32B32_FLOAT;
	polygonLayout[0].InputSlot = 0;
	polygonLayout[0].AlignedByteOffset = 0;
	polygonLayout[0].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
	polygonLayout[0].InstanceDataStepRate = 0;

	polygonLayout[1].SemanticName = "COLOR";
	polygonLayout[1].SemanticIndex = 0;
	polygonLayout[1].Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
	polygonLayout[1].InputSlot = 0;
	polygonLayout[1].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT;
	polygonLayout[1].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
	polygonLayout[1].InstanceDataStepRate = 0;

	// Get a count of the elements in the layout.
	unsigned int numElements = sizeof(polygonLayout) / sizeof(polygonLayout[0]);

	// Create the vertex input layout.
	ID3D11InputLayout* layout = NULL;


	HRESULT result = device->GetDevice()->CreateInputLayout( polygonLayout, 
												numElements, 
												mShader->GetVertexShaderBuffer()->GetBufferPointer(), 
												mShader->GetVertexShaderBuffer()->GetBufferSize(), 
												&layout);
	
	mShader->SetLayout(layout);
	CHECK(result)
	
	// Release the vertex shader buffer and pixel shader buffer since they are no longer needed.
	mShader->GetVertexShaderBuffer()->Release();
	mShader->GetPixelShaderBuffer()->Release();

	return true;
}

bool ColorMaterial::SetShaderParameters(D3DDevice* device, D3DXMATRIX worldMatrix, D3DXMATRIX viewMatrix, D3DXMATRIX projectionMatrix)
{
	bool result = mShader->SetShaderParameters(device, worldMatrix, viewMatrix, projectionMatrix);
	return result;
}

void ColorMaterial::Update(Graphics::RenderableEntity* entity)
{
}

void ColorMaterial::Render(Graphics::RenderableEntity* entity)
{
	Graphics::D3DDevice* device = GetPlatform()->GetDevice()->As<Graphics::D3DDevice>();
	D3DXMATRIX worldMatrix, viewMatrix, projectionMatrix;

	// Get the world, view, and ortho matrices from the camera and d3d objects.
	D3DXMatrixIdentity(&worldMatrix);
	viewMatrix = Utility::NeroToDXD(GetPlatform()->GetCamera()->GetViewMatrix());
	projectionMatrix = device->GetProjectionMatrix();

	Graphics::D3DDevice* nDevice = GetPlatform()->GetDevice()->As<Graphics::D3DDevice>();

	// Put the model vertex and index buffers on the graphics pipeline to prepare them for drawing.
	entity->Render();
	
	// Render the model using the color shader.
	//mShader->Render(nDevice->GetDeviceContext(), entity->GetIndexCount());
}

#elif defined(DX_9)
ColorMaterial::ColorMaterial()
	:Material("ColorMaterial","not used"),
	 MATERIAL_PARAM_INITIALIZATION(WorldViewProjection)
{
}

ColorMaterial::~ColorMaterial()
{
}

void ColorMaterial::Initialize()
{
	Material::Initialize();
	mShader->Initialize();

	HRESULT hr;
	D3DXHANDLE parameterHandle;
	for(int i = 0; ; i++)
	{
		parameterHandle = mShader->GetEffect()->GetParameter(NULL, i);
		if(!parameterHandle)
		{
			break;
		}

		D3DXPARAMETER_DESC parameterDescription;
		hr = mShader->GetEffect()->GetParameterDesc(parameterHandle,&parameterDescription);
		CHECK(hr);

		UINT count = 0;
		switch(parameterDescription.Class)
		{
			case D3DXPC_SCALAR:
				break;
			case  D3DXPC_VECTOR:
				count = parameterDescription.Elements;
				break;
			case D3DXPC_MATRIX_ROWS:
			case D3DXPC_MATRIX_COLUMNS:
				count = parameterDescription.Columns;
				break;
			case D3DXPC_OBJECT:
				count =  1;
				break;
			case D3DXPC_STRUCT:
				break;
			default:
			//	throw RendException("Win32Material::SetParameter::Type Not Supported",__FILE__,__LINE__);
			return;
		}

		Graphics::ShaderParameter* newEffectParameter = MY_NEW(Memory::HID_Rendering, "ShaderParameter") ShaderParameter(this,parameterHandle,parameterDescription,NULL,count);
		mParameters.insert(ParameterPair(newEffectParameter->GetDescription().Name,newEffectParameter));
	}

	MATERIAL_PARAM_RETRIEVE(WorldViewProjection)
}

void ColorMaterial::Deinitialize()
{
}

void ColorMaterial::Update(Graphics::RenderableEntity* entity)
{
	this->WorldViewProjection() << &entity->WorldViewProj();
}


void ColorMaterial::Render(Graphics::RenderableEntity* entity)
{
	BeginRender();
	entity->Render();
	EndRender();
}

void ColorMaterial::BeginRender()
{
	Graphics::D3DDevice* nDevice = GetPlatform()->GetDevice()->As<Graphics::D3DDevice>();
	nDevice->GetDevice()->SetRenderState(D3DRS_LIGHTING, TRUE); 
	nDevice->GetDevice()->SetRenderState(D3DRS_CULLMODE , D3DCULL_NONE);

	mShader->Begin();
	for(UINT j = 0; j < mShader->PassCount(); j++)
	{
		mShader->GetEffect()->BeginPass(j);
	}

}

void ColorMaterial::EndRender()
{
	for(UINT j = 0; j < mShader->PassCount(); j++)
	{
		mShader->GetEffect()->EndPass();
	}	
	mShader->End();
}

MATERIAL_PARAM_DEFINITION(ColorMaterial, WorldViewProjection)
#endif